package server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;

/**
 *
 * This class's main task is to take care of clients problems and other stuff.
 *
 */
public class WorkerRunnable implements Runnable {
	protected Socket clientSocket = null;
	String serverText = null;
	BufferedReader bufferReader;
	BufferedWriter bufferWriter;
	PrintWriter out = null;
	public int id;
	Table t;
	WorkerRunnable[] w;

	public WorkerRunnable(Socket clientSocket, String serverText, Server s) {
		this.clientSocket = clientSocket;
		this.serverText = serverText;
		// add table
		this.t = s.t;
		// add new player
		id = t.addPlayer("");
		this.w = s.w;
	}

	public void run() {
		try {
			createCommunication();
			while (true) {
				parseAction(splitCommand(receive()));
			}
		} catch (IOException e) {
		}
	}

	public String[] splitCommand(String command) {
		String[] s = command.split(" ");
		return s;
	}

	public void parseAction(String[] command) throws IOException {
		int value;
		int nid;

		switch (command[0]) {
		case "nick":
			t.players[id].setNick(command[1]);
			// send new player information to everyone
			sendToAllExceptClient(new String[] { id + "",
					t.players[id].getNick(), "newplayer",
					t.players[id].getID() + "", t.players[id].balance + "",
					t.players[id].currentBet + "" });
			// send player information to client (id, nick, account balance)
			sendToClient(new String[] { id + "", t.players[id].getNick(),
					"playerinfo", t.players[id].getID() + "",
					t.players[id].balance + "", t.players[id].currentBet + "" });
			// send message about other players to client
			for (int i = 0; i < t.players.length; i++) {
				if (t.isPlayer(t.players[i]) && i != id)
					sendToClient(new String[] { id + "",
							t.players[i].getNick(), "opponentinfo",
							t.players[i].getID() + "",
							t.players[i].balance + "",
							t.players[i].currentBet + "" });
			}
			break;
		case "disconnect":
			sendToAllExceptClient(new String[] { id + "",
					t.players[id].getNick(), "disconnect",
					t.players[id].getID() + "" });
			t.players[id] = null;
			break;
		case "start":
			if (t.playersCount() > 1) {
				// print information to console
				System.out.println("[---]Game started, going to round #0");
				// start game
				t.start();
				// allow small blind to bet
				sendToPlayer(
						preparePermission(t.canDoAction(t.getSmallBlind()),
								t.getSmallBlind().id), t.getSmallBlind().id);
				// send acknowledgement to everyone with blinds ID
				sendToAll(new String[] { id + "", t.players[id].getNick(),
						"start", "ok", t.getSmallBlind().id + "" });

			} else {
				// send "error" (information only for client who has privilege
				// to start game
				sendToClient(new String[] { id + "", t.players[id].getNick(),
						"start", "err", });
			}

			break;
		case "exchange":
			// set number of exchanged cards
			int exchangedCards = 0;
			// exchange cards
			for (int i = 1; i < 5; i++)
				if ("1".equals(command[i])) {
					t.Exchange(t.players[id], i - 1);
					exchangedCards++;
				}
			// set "hasExchanged" to true
			t.players[id].hasExchanged = true;
			// send to all info about exchange
			sendToAll(new String[] { id + "", t.players[id].getNick(),
					"exchange", exchangedCards + "" });
			// send to client new cards
			sendToClient(new String[] {
					id + "",
					t.players[id].nick,
					"cards",
					t.players[id].hand.cards[0].suit + "-"
							+ t.players[id].hand.cards[0].value,
					t.players[id].hand.cards[1].suit + "-"
							+ t.players[id].hand.cards[1].value,
					t.players[id].hand.cards[2].suit + "-"
							+ t.players[id].hand.cards[2].value,
					t.players[id].hand.cards[3].suit + "-"
							+ t.players[id].hand.cards[3].value });
			// send to client permission
			sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, id);
			// if everyone exchanged cards, allow small blind to bet
			if (t.everyoneExchanged()) {
				sendPermission(t.canDoAction(t.getSmallBlind()),
						t.getSmallBlind().id);
				// go to next round
				t.round++;
				// reset exchanges
				t.resetHasExchanged();
				// print to server
				System.out.println("[---]Exchange finished, going to round #"
						+ t.round);
			} else {
				nid = t.getNextPlayer(t.players[id]).id;
				sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 1 }, nid);
			}
			break;
		case "bet":
			value = Integer.parseInt(command[1]);
			try {
				// do action
				t.players[id].Bet(t, value);
				// send to all info about bet
				sendToAll(new String[] { id + "", t.players[id].getNick(),
						"bet", t.players[id].currentBet + "",
						t.players[id].balance + "", t.pot + "", t.maxBet + "" });
				// increase done actions in table
				t.doneActions++;
				t.doneActionsInRound++;
				// send to client permission
				sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, id);
				// send to next player permission (next player is Big
				// Blind, so he can't just check
				nid = t.getNextPlayer(t.players[id]).id;
				sendPermission(new int[] { 0, 1, 0, 0, 1, 1, 0 }, nid);
			} catch (Exception e) {
				// send to client if bet was too high
				sendToClient(new String[] { id + "", t.players[id].getNick(),
						"beterr" });
			}
			break;
		case "raise":
			value = Integer.parseInt(command[1]);
			try {
				// do action
				t.players[id].Raise(t, value);
				// send to all info about raise
				sendToAll(new String[] { id + "", t.players[id].getNick(),
						"raise", t.players[id].currentBet + "",
						t.players[id].balance + "", t.pot + "", t.maxBet + "" });
				// increase done actions in table
				t.doneActions++;
				t.doneActionsInRound++;
				// send to client permission
				sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, id);
				// send to next player permission
				nid = t.getNextPlayer(t.players[id]).id;
				sendPermission(t.canDoAction(t.players[nid]), nid);
			} catch (Exception e) {
				// send to client if raise was too high
				sendToClient(new String[] { id + "", t.players[id].getNick(),
						"beterr" });
			}
			break;
		case "check":
			// do action
			t.players[id].Check();
			// send to all info about call
			sendToAll(new String[] { id + "", t.players[id].getNick(), "check",
					t.players[id].currentBet + "", t.players[id].balance + "",
					t.pot + "", t.maxBet + "" });
			// increase done actions in table
			t.doneActions++;
			t.doneActionsInRound++;
			// send to client permission
			sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, id);
			// send to next player permission
			nid = t.getNextPlayer(t.players[id]).id;
			sendPermission(t.canDoAction(t.players[nid]), nid);
			break;
		case "call":
			// do action
			t.players[id].Call(t);
			// send to all info about call
			sendToAll(new String[] { id + "", t.players[id].getNick(), "call",
					t.players[id].currentBet + "", t.players[id].balance + "",
					t.pot + "", t.maxBet + "" });
			// increase done actions in table
			t.doneActions++;
			t.doneActionsInRound++;
			// send to client permission
			sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, id);
			// send to next player permission
			nid = t.getNextPlayer(t.players[id]).id;
			sendPermission(t.canDoAction(t.players[nid]), nid);
			break;
		case "allin":
			// do action
			t.players[id].AllIn(t);
			// send to all info about call
			sendToAll(new String[] { id + "", t.players[id].getNick(), "allin",
					t.players[id].currentBet + "", t.players[id].balance + "",
					t.pot + "", t.maxBet + "" });
			// increase done actions in table
			t.doneActions++;
			t.doneActionsInRound++;
			// send to client permission
			sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, id);
			// send to next player permission
			nid = t.getNextPlayer(t.players[id]).id;
			sendPermission(t.canDoAction(t.players[nid]), nid);
			break;
		case "fold":
			// do action
			t.players[id].Fold();
			// send to all info about call
			sendToAll(new String[] { id + "", t.players[id].getNick(), "fold",
					t.players[id].currentBet + "", t.players[id].balance + "",
					t.pot + "", t.maxBet + "" });
			// increase done actions in table
			t.doneActions++;
			t.doneActionsInRound++;
			// send to client permission
			sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, id);
			// send to next player permission
			nid = t.getNextPlayer(t.players[id]).id;
			sendPermission(t.canDoAction(t.players[nid]), nid);
			break;
		}

		// after 2nd action, give cards
		if (t.doneActions == 2) {
			if (command[0].equals("raise") || command[0].equals("fold")
					|| command[0].equals("allin")) {
				// send card info to all players
				for (int i = 0; i < t.players.length; i++) {
					if (t.players[i] != null) {
						sendToPlayer(new String[] {
								i + "",
								t.players[i].nick,
								"cards",
								t.players[i].hand.cards[0].suit + "-"
										+ t.players[i].hand.cards[0].value,
								t.players[i].hand.cards[1].suit + "-"
										+ t.players[i].hand.cards[1].value,
								t.players[i].hand.cards[2].suit + "-"
										+ t.players[i].hand.cards[2].value,
								t.players[i].hand.cards[3].suit + "-"
										+ t.players[i].hand.cards[3].value }, i);
					}
				}
			}
		}

		// if everyone has same bet and made action at least once, go to next
		// round
		if (t.everyoneHasSameBet() && (t.round % 2) == 0 && t.round <= 6
				&& t.doneActionsInRound >= t.playersCountPlaying()) {
			// go to next round
			t.round++;
			// reset bets
			t.resetDoneActionsInRound();
			// remove permission
			for (int i = 0; i < t.players.length; i++) {
				if (t.players[i] != null) {
					sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, i);
				}
			}
			// allow small blind to exchange
			sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 1 },
					t.getSmallBlind().id);
			// print information to server
			System.out.println("[---]Betting finished, going to round #"
					+ t.round);
		}

		if (t.round == 7) {
			System.out.println("[---]Game over");
			// finalize match (give money to winner)
			t.FinalizeMatch();
			// remove permission
			for (int i = 0; i < t.players.length; i++) {
				if (t.players[i] != null) {
					sendPermission(new int[] { 0, 0, 0, 0, 0, 0, 0 }, i);
				}
			}
			// send info about winner to clients
			sendToAll(new String[] { t.getWinner().getID() + "",
					t.getWinner().getNick(), "winner",
					t.getWinner().balance + "" });
			// print to server
			System.out.println("[---]Winner: " + t.getWinner().getNick());
			System.out.println("[---]Game started, going to round #0");
			// reset everything
			t.start();
			// allow small blind to bet
			sendPermission(t.canDoAction(t.getSmallBlind()),
					t.getSmallBlind().id);
		}

	}

	/**
	 * Send message to client
	 * 
	 * @param text
	 * @throws IOException
	 */
	public void sendToClient(String[] text) throws IOException {
		out.println(implode(text));
	}

	/**
	 * Send message to chosen player
	 * 
	 * @param id
	 * @param text
	 * @throws IOException
	 */
	public void sendToPlayer(String[] text, int id) throws IOException {
		if (w[id] != null) {
			w[id].out.println(implode(text));
		}
	}

	/**
	 * Send message to everyone
	 * 
	 * @param text
	 * @throws IOException
	 */
	public void sendToAll(String[] text) throws IOException {
		for (int i = 0; i < w.length; i++) {
			sendToPlayer(text, i);
		}
		System.out.println("[action] " + implode(text));
	}

	/**
	 * Send message to everyone except client
	 * 
	 * @param text
	 * @throws IOException
	 */
	public void sendToAllExceptClient(String[] text) throws IOException {
		for (int i = 0; i < w.length; i++) {
			if (i != id)
				sendToPlayer(text, i);
		}
		System.out.println("[action] " + implode(text));
	}

	/**
	 * Connect all strings in array into one string
	 * 
	 * @param text
	 * @return
	 */
	private String implode(String[] text) {
		String s = "";
		for (int i = 0; i < text.length; i++) {
			s += text[i];
			if (i < text.length - 1)
				s += " ";
		}
		return s;
	}

	public String[] preparePermission(int[] permission, int playerID) {
		String[] perm = new String[permission.length + 3];
		perm[0] = playerID + "";
		perm[1] = t.players[playerID].nick;
		perm[2] = "permission";
		for (int i = 0; i < permission.length; i++) {
			perm[i + 3] = permission[i] + "";
		}
		return perm;
	}

	public void sendPermission(int[] permission, int playerID)
			throws IOException {
		sendToPlayer(preparePermission(permission, playerID), playerID);
	}

	public String receive() throws IOException {
		return bufferReader.readLine();
	}

	public void createCommunication() throws IOException {
		// To receive information from the client.
		bufferReader = new BufferedReader(new InputStreamReader(
				clientSocket.getInputStream()));
		// To sent information to the client bufferWriter = new
		out = new PrintWriter(clientSocket.getOutputStream(), true);

	}
}
